Package org.python.pydev.editor.codecompletion.revisited.visitors

Source Code of org.python.pydev.editor.codecompletion.revisited.visitors.FindDefinitionModelVisitor

/**
* Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved.
* Licensed under the terms of the Eclipse Public License (EPL).
* Please see the license.txt included with this distribution for details.
* Any modifications to this file must keep this entire header intact.
*/
/*
* Created on Jan 19, 2005
*
* @author Fabio Zadrozny
*/
package org.python.pydev.editor.codecompletion.revisited.visitors;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.Stack;

import org.python.pydev.core.ILocalScope;
import org.python.pydev.core.IModule;
import org.python.pydev.core.docutils.PySelection;
import org.python.pydev.core.structure.FastStack;
import org.python.pydev.parser.jython.SimpleNode;
import org.python.pydev.parser.jython.ast.Assign;
import org.python.pydev.parser.jython.ast.Call;
import org.python.pydev.parser.jython.ast.ClassDef;
import org.python.pydev.parser.jython.ast.FunctionDef;
import org.python.pydev.parser.jython.ast.Global;
import org.python.pydev.parser.jython.ast.ImportFrom;
import org.python.pydev.parser.jython.ast.Module;
import org.python.pydev.parser.jython.ast.Name;
import org.python.pydev.parser.jython.ast.NameTok;
import org.python.pydev.parser.jython.ast.NameTokType;
import org.python.pydev.parser.jython.ast.Subscript;
import org.python.pydev.parser.jython.ast.Tuple;
import org.python.pydev.parser.jython.ast.aliasType;
import org.python.pydev.parser.jython.ast.exprType;
import org.python.pydev.parser.visitors.NodeUtils;

/**
* @author Fabio Zadrozny
*/
public class FindDefinitionModelVisitor extends AbstractVisitor {

    /**
     * This is the token to find.
     */
    private String tokenToFind;

    /**
     * List of definitions.
     */
    public List<Definition> definitions = new ArrayList<Definition>();

    /**
     * Stack of classes / methods to get to a definition.
     */
    private FastStack<SimpleNode> defsStack = new FastStack<SimpleNode>(20);

    /**
     * This is a stack that will keep the globals for each stack
     */
    private FastStack<Set<String>> globalDeclarationsStack = new FastStack<Set<String>>(20);

    /**
     * This is the module we are visiting: just a weak reference so that we don't create a cycle (let's
     * leave things easy for the garbage collector).
     */
    private WeakReference<IModule> module;

    /**
     * It is only available if the cursor position is upon a NameTok in an import (it represents the complete
     * path for finding the module from the current module -- it can be a regular or relative import).
     */
    public String moduleImported;

    private int line;

    private int col;

    private boolean foundAsDefinition = false;

    private Definition definitionFound;

    /**
     * Call is stored for the context for a keyword parameter
     */
    private Stack<Call> call = new Stack<Call>();

    /**
     * Constructor
     * @param line: starts at 1
     * @param col: starts at 1
     */
    public FindDefinitionModelVisitor(String token, int line, int col, IModule module) {
        this.tokenToFind = token;
        this.module = new WeakReference<IModule>(module);
        this.line = line;
        this.col = col;
        this.moduleName = module.getName();
        //we may have a global declared in the global scope
        globalDeclarationsStack.push(new HashSet<String>());
    }

    @Override
    public Object visitImportFrom(ImportFrom node) throws Exception {
        String modRep = NodeUtils.getRepresentationString(node.module);
        if (NodeUtils.isWithin(line, col, node.module)) {
            //it is a token in the definition of a module
            int startingCol = node.module.beginColumn;
            int endingCol = startingCol;
            while (endingCol < this.col) {
                endingCol++;
            }
            int lastChar = endingCol - startingCol;
            moduleImported = modRep.substring(0, lastChar);
            int i = lastChar;
            while (i < modRep.length()) {
                if (Character.isJavaIdentifierPart(modRep.charAt(i))) {
                    i++;
                } else {
                    break;
                }
            }
            moduleImported += modRep.substring(lastChar, i);
        } else {
            //it was not the module, so, we have to check for each name alias imported
            for (aliasType alias : node.names) {
                //we do not check the 'as' because if it is some 'as', it will be gotten as a global in the module
                if (NodeUtils.isWithin(line, col, alias.name)) {
                    moduleImported = modRep + "." + NodeUtils.getRepresentationString(alias.name);
                }
            }
        }
        return super.visitImportFrom(node);
    }

    /**
     * @see org.python.pydev.parser.jython.ast.VisitorBase#unhandled_node(org.python.pydev.parser.jython.SimpleNode)
     */
    protected Object unhandled_node(SimpleNode node) throws Exception {
        return null;
    }

    /**
     * @see org.python.pydev.parser.jython.ast.VisitorBase#traverse(org.python.pydev.parser.jython.SimpleNode)
     */
    public void traverse(SimpleNode node) throws Exception {
        node.traverse(this);
    }

    /**
     * @see org.python.pydev.parser.jython.ast.VisitorBase#visitClassDef(org.python.pydev.parser.jython.ast.ClassDef)
     */
    public Object visitClassDef(ClassDef node) throws Exception {
        globalDeclarationsStack.push(new HashSet<String>());
        defsStack.push(node);

        node.traverse(this);

        defsStack.pop();
        globalDeclarationsStack.pop();

        checkDeclaration(node, (NameTok) node.name);
        return null;
    }

    /**
     * @see org.python.pydev.parser.jython.ast.VisitorBase#visitFunctionDef(org.python.pydev.parser.jython.ast.FunctionDef)
     */
    public Object visitFunctionDef(FunctionDef node) throws Exception {
        globalDeclarationsStack.push(new HashSet<String>());
        defsStack.push(node);

        if (node.args != null) {
            if (node.args.args != null) {
                for (exprType arg : node.args.args) {
                    if (arg instanceof Name) {
                        checkParam((Name) arg);
                    }
                }
            }
            if (node.args.kwonlyargs != null) {
                for (exprType arg : node.args.kwonlyargs) {
                    if (arg instanceof Name) {
                        checkParam((Name) arg);
                    }
                }
            }
        }
        node.traverse(this);

        defsStack.pop();
        globalDeclarationsStack.pop();

        checkDeclaration(node, (NameTok) node.name);
        return null;
    }

    /**
     * @param node the declaration node we're interested in (class or function)
     * @param name the token that represents the name of that declaration
     */
    private void checkParam(Name name) {
        String rep = NodeUtils.getRepresentationString(name);
        if (rep.equals(tokenToFind) && line == name.beginLine && col >= name.beginColumn
                && col <= name.beginColumn + rep.length()) {
            foundAsDefinition = true;
            // if it is found as a definition it is an 'exact' match, so, erase all the others.
            ILocalScope scope = new LocalScope(this.defsStack);
            for (Iterator<Definition> it = definitions.iterator(); it.hasNext();) {
                Definition d = it.next();
                if (!d.scope.equals(scope)) {
                    it.remove();
                }
            }

            definitionFound = new Definition(line, name.beginColumn, rep, name, scope, module.get());
            definitions.add(definitionFound);
        }
    }

    @Override
    public Object visitCall(Call node) throws Exception {
        this.call.push(node);
        Object r = super.visitCall(node);
        this.call.pop();
        return r;
    }

    @Override
    public Object visitNameTok(NameTok node) throws Exception {
        if (node.ctx == NameTok.KeywordName) {
            if (this.line == node.beginLine && this.call.size() > 0) {
                String rep = NodeUtils.getRepresentationString(node);

                if (PySelection.isInside(col, node.beginColumn, rep.length())) {
                    foundAsDefinition = true;
                    // if it is found as a definition it is an 'exact' match, so, erase all the others.
                    ILocalScope scope = new LocalScope(this.defsStack);
                    for (Iterator<Definition> it = definitions.iterator(); it.hasNext();) {
                        Definition d = it.next();
                        if (!d.scope.equals(scope)) {
                            it.remove();
                        }
                    }

                    definitions.clear();

                    definitionFound = new KeywordParameterDefinition(line, node.beginColumn, rep, node, scope,
                            module.get(), this.call.peek());
                    definitions.add(definitionFound);
                    throw new StopVisitingException();
                }
            }
        }
        return null;
    }

    /**
     * @param node the declaration node we're interested in (class or function)
     * @param name the token that represents the name of that declaration
     */
    private void checkDeclaration(SimpleNode node, NameTok name) {
        String rep = NodeUtils.getRepresentationString(node);
        if (rep.equals(tokenToFind)
                && ((line == -1 && col == -1) || (line == name.beginLine && col >= name.beginColumn && col <= name.beginColumn
                        + rep.length()))) {
            foundAsDefinition = true;
            // if it is found as a definition it is an 'exact' match, so, erase all the others.
            ILocalScope scope = new LocalScope(this.defsStack);
            for (Iterator<Definition> it = definitions.iterator(); it.hasNext();) {
                Definition d = it.next();
                if (!d.scope.equals(scope)) {
                    it.remove();
                }
            }

            definitionFound = new Definition(name.beginLine, name.beginColumn, rep, node, scope, module.get());
            definitions.add(definitionFound);
        }
    }

    @Override
    public Object visitGlobal(Global node) throws Exception {
        for (NameTokType n : node.names) {
            globalDeclarationsStack.peek().add(NodeUtils.getFullRepresentationString(n));
        }
        return null;
    }

    @Override
    public Object visitModule(Module node) throws Exception {
        this.defsStack.push(node);
        return super.visitModule(node);
    }

    /**
     * @see org.python.pydev.parser.jython.ast.VisitorBase#visitAssign(org.python.pydev.parser.jython.ast.Assign)
     */
    public Object visitAssign(Assign node) throws Exception {
        ILocalScope scope = new LocalScope(this.defsStack);
        if (foundAsDefinition && !scope.equals(definitionFound.scope)) { //if it is found as a definition it is an 'exact' match, so, we do not keep checking it
            return null;
        }

        for (int i = 0; i < node.targets.length; i++) {
            exprType target = node.targets[i];
            if (target instanceof Subscript) {
                continue; //assigning to an element and not the variable itself. E.g.: mydict[1] = 10 (instead of mydict = 10)
            }

            if (target instanceof Tuple) {
                //if assign is xxx, yyy = 1, 2
                //let's separate those as different assigns and analyze one by one
                Tuple targetTuple = (Tuple) target;
                if (node.value instanceof Tuple) {
                    Tuple valueTuple = (Tuple) node.value;
                    checkTupleAssignTarget(targetTuple, valueTuple.elts);

                } else if (node.value instanceof org.python.pydev.parser.jython.ast.List) {
                    org.python.pydev.parser.jython.ast.List valueList = (org.python.pydev.parser.jython.ast.List) node.value;
                    checkTupleAssignTarget(targetTuple, valueList.elts);

                } else {
                    checkTupleAssignTarget(targetTuple, new exprType[] { node.value });
                }

            } else {
                String rep = NodeUtils.getFullRepresentationString(target);

                if (tokenToFind.equals(rep)) { //note, order of equals is important (because one side may be null).
                    exprType nodeValue = node.value;
                    String value = NodeUtils.getFullRepresentationString(nodeValue);
                    if (value == null) {
                        value = "";
                    }

                    //get the line and column correspondent to the target
                    int line = NodeUtils.getLineDefinition(target);
                    int col = NodeUtils.getColDefinition(target);

                    AssignDefinition definition = new AssignDefinition(value, rep, i, node, line, col, scope,
                            module.get(), nodeValue);

                    //mark it as global (if it was found as global in some of the previous contexts).
                    for (Set<String> globals : globalDeclarationsStack) {
                        if (globals.contains(rep)) {
                            definition.foundAsGlobal = true;
                        }
                    }

                    definitions.add(definition);
                }
            }
        }

        return null;
    }

    /**
     * Analyze an assign that has the target as a tuple and the multiple elements in the other side.
     *
     * E.g.: www, yyy = 1, 2
     *
     * @param targetTuple the target in the assign
     * @param valueElts the values that are being assigned
     */
    private void checkTupleAssignTarget(Tuple targetTuple, exprType[] valueElts) throws Exception {
        if (valueElts == null || valueElts.length == 0) {
            return; //nothing to do if we don't have any values
        }

        for (int i = 0; i < targetTuple.elts.length; i++) {
            int j = i;
            //that's if the number of values is less than the number of assigns (actually, that'd
            //probably be an error, but let's go on gracefully, as the user can be in an invalid moment
            //in his code)
            if (j >= valueElts.length) {
                j = valueElts.length - 1;
            }
            Assign assign = new Assign(new exprType[] { targetTuple.elts[i] }, valueElts[j]);
            assign.beginLine = targetTuple.beginLine;
            assign.beginColumn = targetTuple.beginColumn;
            visitAssign(assign);
        }
    }
}
TOP

Related Classes of org.python.pydev.editor.codecompletion.revisited.visitors.FindDefinitionModelVisitor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.